﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ThiefRun.Util.Graph;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ThiefRun.States
{
    public class StateMachine : Graph<State>
    {
        #region Constants
        // Constants
        #endregion Constants

        #region Properties
        // Properties
        Node<State> current;
        #endregion Properties

        #region Computed Properties
        // Computed Properties
        public Node<State> GetCurrent
        {
            get { return current; }
        }
        public State SetCurrent
        {
            set
            {
                bool found = false;
                foreach (Node<State> node in nodes)
                {
                    // The node was found
                    if (node.Data == value)
                    {
                        this.current = node;
                        found = true;
                        break;
                    }
                }

                // The node was not found.
                if (!found)
                    this.current = null; 
            }
        }
        #endregion

        #region Methods
        // Methods

        /// <summary>
        /// Initialize the Stage Graph.
        /// </summary>
        public StateMachine() : base()
        {
            this.current = null;
        }

        /// <summary>
        /// Draw the current stage as long as there is one.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="spriteBatch"></param>
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            // As long the graph is pointing to something, draw the current stage.
            if (current != null)
                current.Data.Draw(gameTime, spriteBatch);
        }

        /// <summary>
        /// Perform the actions of the stage graph.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            // As long the graph is pointing to something, perform the actions.
            if (current != null)
            {
                // Get all possible transitons.
                Transition transition;
                List<Edge<Node<State>>> adjEdges = AdjacentEdges(current);

                // Choose the first transition that is satisfied.
                foreach (Edge<Node<State>> adjEdge in adjEdges)
                {
                    transition = (Transition)adjEdge;

                    // The conditions are meet so change the state.
                    if (transition.Condition(transition.Left.Data))
                    {
                        current = transition.Right;
                        break;
                    }
                }

                current.Data.Update(gameTime);
            }
        }

        /// <summary>
        /// Add an edge between two nodes.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        public void AddTransition(State left, State right, Transition.Conditions conditions)
        {
            Node<State> leftNode = FindNode(left);
            Node<State> rightNode = FindNode(right);

            Transition transition = new Transition(leftNode, rightNode, conditions);
            edges.Add(transition);
        }

        /// <summary>
        /// Find the edges adjacent to the parameter.
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private Node<State> FindNode(State data)
        {
            foreach (Node<State> node in nodes)
            {
                // The node was found.
                if (node.Data == data)
                {
                    return node;
                }
            }

            return null;
        }
        #endregion Methods
    }
}
